package alpha.algorithm;

import alpha.com.Processor;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * Created by Justin on 2015/10/9.
 */
public class ClassCenterDistance implements Processor<int[][], Vector<Set<int[]>>> {
    private int distanceMode;
    private final String MethodName = "ClassCenterDistance";

    @Override
    public String getMethodName() {
        return MethodName;
    }

    @Override
    public int[][] run(Vector<Set<int[]>> input, Object... params) {
//        return serialProcessing(input, params);
        return parallelProcessing(input,params);
    }

    private int[][] serialProcessing(Vector<Set<int[]>> input, Object... params) {
        //读入参数
        getParams(params);

        int[][] result = new int[400][400];

        //处理开始。。。
        /* ***************************************** */

        //进行数据处理
        //判断几种颜色的数据输入
        Vector<Integer> vaildDataIndex = new Vector<Integer>();
        for (int i = 0; i < 3; i++) {
            if (input.get(i).size() > 0) {
                vaildDataIndex.add(i);
            }
        }

        Map<Integer, int[]> center = new HashMap<Integer, int[]>();
        //最简单的类中心方法
        for (int i : vaildDataIndex) {
            int[] c = new int[2];
            int x = 0;
            int y = 0;
            for (int[] point : input.get(i)) {
                x += point[0];
                y += point[1];
            }
            c[0] = (int) (x / input.get(i).size());
            c[1] = (int) (y / input.get(i).size());
            center.put(i, c);
        }

        double d, k;
        int minLabel;
        int[] c;
        for (int x = 0; x < 400; x++) {
            for (int y = 0; y < 400; y++) {
                d = 1000;
                minLabel = -1;
                for (int i : vaildDataIndex) {
                    c = center.get(i);
                    //k = Math.abs(x - c[0]) + Math.abs(y - c[1]);
                    k = distance(x, y, c[0], c[1]);
                    if (d > k) {
                        d = k;
                        minLabel = i;
                    }
                }

                result[x][y] = minLabel;
            }
        }

        /* ****************************************  */
        //处理结束。。。
        return result;
    }

    private int[][] parallelProcessing(Vector<Set<int[]>> input, Object... params) {
        //读入参数
        getParams(params);

        int[][] result = new int[400][400];

        //进行数据处理
        //判断几种颜色的数据输入
        Vector<Integer> vaildDataIndex = new Vector<Integer>();
        for (int i = 0; i < 3; i++) {
            if (input.get(i).size() > 0) {
                vaildDataIndex.add(i);
            }
        }

        Map<Integer, int[]> center = new HashMap<Integer, int[]>();
        //最简单的类中心方法
        for (int i : vaildDataIndex) {
            int[] c = new int[2];
            int x = 0;
            int y = 0;
            for (int[] point : input.get(i)) {
                x += point[0];
                y += point[1];
            }
            c[0] = (int) (x / input.get(i).size());
            c[1] = (int) (y / input.get(i).size());
            center.put(i, c);
        }

        ForkJoinPool forkJoinPool = new ForkJoinPool();


        forkJoinPool.invoke(new mTask(result,400,center,vaildDataIndex));
        //forkJoinPool.submit(new mTask(result,400,center,vaildDataIndex));//这是异步处理的
        //如果时间设定太短，而任务分块太少，结果就不稳定了，因为有些分块没有算完
        //forkJoinPool.awaitQuiescence(2,TimeUnit.MINUTES);

        forkJoinPool.shutdown();

        return result;
    }

    private void getParams(Object... params) {
        distanceMode = 0;
        for (Object obj : params) {
            if (obj instanceof Integer) {
                distanceMode = ((Integer) obj).intValue();
                break;
            }
        }
    }

    private final double distance(int x1, int y1, int x2, int y2) {

        switch (distanceMode) {
            case 0:
                return Math.abs(x1 - x2) + Math.abs(y1 - y2);
            case 1:
                return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
            case 2:
                return Math.max((Math.abs(x1 - x2)), (Math.abs(y1 - y2)));
            default:
                return Math.abs(x1 - x2) + Math.abs(y1 - y2);
        }

    }

    public class mTask extends RecursiveAction {
        private int[][] array;
        private int startRow;
        private int endRow;
        private int col;
        private Map<Integer, int[]> center;
        private Vector<Integer> vaildDataIndex;
        private int THRESHOLD = 21;

        public mTask(int[][] array, int col, Map<Integer, int[]> center, Vector<Integer> vaildDataIndex) {
            this.array = array;
            this.startRow = 0;
            this.endRow = array.length;
            this.col = col;
            this.center = center;
            this.vaildDataIndex = vaildDataIndex;
        }

        public mTask(int[][] array, int start, int end, int col, Map<Integer, int[]> center, Vector<Integer> vaildDataIndex) {
            this.array = array;
            this.startRow = start;
            this.endRow = end;
            this.col = col;
            this.center = center;
            this.vaildDataIndex = vaildDataIndex;
        }

        @Override
        protected void compute() {
            if (endRow - startRow < THRESHOLD) {
                double d, k;
                int minLabel;
                int[] c;
                for (int x = startRow; x < endRow; x++) {
                    for (int y = 0; y < col; y++) {
                        d = 1000;
                        minLabel = -1;
                        for (int i : vaildDataIndex) {
                            c = center.get(i);
                            k = distance(x, y, c[0], c[1]);
                            if (d > k) {
                                d = k;
                                minLabel = i;
                            }
                        }
                        array[x][y] = minLabel;
                    }
                }
            } else {
                int half = (endRow + startRow) >> 1;
//                new mTask(array, startRow, half, col, center, vaildDataIndex).fork();
//                new mTask(array, half, endRow, col, center, vaildDataIndex).fork();
                new mTask(array, startRow, half, col, center, vaildDataIndex).invoke();
                new mTask(array, half, endRow, col, center, vaildDataIndex).invoke();
            }
        }
    }

}
